﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AL100.BankApplication.BLL
{
    /// <summary>
    /// Abstract base class for all accounts.
    /// </summary>
    public abstract class AccountBase
    {

        private double _balance;
        private IDictionary<Guid, ICommand> _commands;




        /*********************************************************************************
          * 
          *  Events
          * 
          *********************************************************************************/

        public event EventHandler<AccountEventArgs> OverdrawnWithdrawal;


        /*********************************************************************************
         * 
         *  Constructors
         * 
         *********************************************************************************/

        /// <summary>
        /// Initializes a new instance of the <see cref="AccountBase"/> class.
        /// </summary>
        /// <param name="holder">The holder.</param>
        /// <param name="initialDeposit">The initial deposit.</param>
        public AccountBase(Customer holder, double initialDeposit)
        {
            Holder = holder;

            //on instancie le dictionnaire des commandes.
            _commands = new Dictionary<Guid,ICommand>();

            // Deposits the specified amount by calling the Deposit method.
            Deposit(initialDeposit);

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AccountBase"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="firstName">The first name.</param>
        /// <param name="initialDeposit">The initial deposit.</param>
        public AccountBase(string name, string firstName, double initialDeposit)
            : this(new Customer(name, firstName), initialDeposit)
        { }

        /*********************************************************************************
         * 
         *  Methods
         * 
         *********************************************************************************/

        /// <summary>
        /// Runs the strategy.
        /// </summary>
        /// <param name="strategy">The strategy.</param>
        public void RunStrategy(StrategyBase<AccountBase> strategy)
        {
            // Exécute la stratégie sur l'instance en cours
            strategy.Execute(this);
        }



        /// <summary>
        /// Gets the max overdrawn amount.
        /// </summary>
        /// <returns>the max overdrawn amount.</returns>
        public abstract double GetMaxOverdrawnAmount();


        /// <summary>
        /// Deposits the specified amount.
        /// </summary>
        /// <param name="amount">The amount.</param>
        public void Deposit(double amount)
        {
            if (amount > 0)
            {                
                StoreAndExecute(Operator.Add, amount);                
            }
        }

       
        /// <summary>
        /// Withdraws the specified amount.
        /// </summary>
        /// <param name="amount">The amount.</param>
        public void Withdraw(double amount)
        {
            if (amount > 0)
            {
                StoreAndExecute(Operator.Substract, amount);
            }          
            
            // Calls the procedure that will fire the event.
            OnOverdrawnWithdrawal();
        }

        /// <summary>
        /// Stores the and executes the specified Command
        /// </summary>
        /// <param name="command">The command.</param>
        private void StoreAndExecute(Operator op, double amount)
        {
            var command = new CommandBase<AccountBase>(this, Operator.Add, amount);
            
            _commands.Add(Guid.NewGuid(), command);
            command.Execute();
        }

        /// <summary>
        /// Called when there is an overdrawn withdrawal
        /// </summary>
        protected virtual void OnOverdrawnWithdrawal()
        {

            // If the account exceeds the authorized overdrawn amount..
            if (Balance < -GetMaxOverdrawnAmount())
            {
                // if there are procedures subscribed to this event..
                if (OverdrawnWithdrawal != null)
                {
                    OverdrawnWithdrawal(this, new AccountEventArgs(Balance,
                                                                   GetMaxOverdrawnAmount(),
                                                                   Balance + GetMaxOverdrawnAmount()));
                }
            }
        }


        /*********************************************************************************
         * 
         *  Properties
         * 
         *********************************************************************************/


        /// <summary>
        /// Gets the holder.
        /// </summary>
        public Customer Holder { get; private set; }


        /// <summary>
        /// Gets the balance.
        /// </summary>
        public double Balance
        {
            get { return _balance; }
            internal set { _balance = value; }
        }

        /// <summary>
        /// Gets the commands.
        /// </summary>
        public IDictionary<Guid, ICommand> Commands
        {
            get { return _commands; }
        }


    }
}
